﻿using System.Linq;
using System.Web.Mvc;
using NearForums.Services;
using NearForums.Web.Extensions;
using NearForums.Validation;
using NearForums.Web.Controllers.Filters;
using NearForums.Web.Controllers.Helpers;
using System.Collections;
using System;
using System.IO;
using System.Web;
using System.Globalization;
using System.Drawing;


namespace NearForums.Web.Controllers
{
	public class TopicsController : BaseController
	{
		/// <summary>
		/// Topic service
		/// </summary>
		private readonly ITopicsService _service;
		/// <summary>
		/// User service
		/// </summary>
		private readonly IUsersService _userService;
		/// <summary>
		/// Forum service
		/// </summary>
		private readonly IForumsService _forumService;
		/// <summary>
		/// Message service
		/// </summary>
		private readonly IMessagesService _messageService;
		/// <summary>
		/// Service that handles the subscriptions
		/// </summary>
		private readonly ITopicsSubscriptionsService _topicSubscriptionService;

		public TopicsController(ITopicsService service, IForumsService forumService, IMessagesService messageService, IUsersService userService, ITopicsSubscriptionsService topicSubscriptionService) : base(userService)
		{
			_service = service;
			_forumService = forumService;
			_messageService = messageService;
			_userService = userService;
			_topicSubscriptionService = topicSubscriptionService;
		}

		#region Detail
		[AddVisit]
		[ValidateReadAccess]
		public ActionResult Detail(int id, string name, string forum, int page)
		{
			var topic = _service.Get(id, name);

			if (topic == null)
			{
				return ResultHelper.NotFoundResult(this);
			}
			if (topic.Forum.ShortName != forum)
			{
				//The topic could have been moved to another forum
				//Move permanently to the other forum's topic
				return ResultHelper.MovedPermanentlyResult(this, new{action="Detail", controller="Topics", id=id, name=name, forum=topic.Forum.ShortName});
			}

			topic.Messages = _messageService.GetByTopic(id);
			//load related topics
			_service.LoadRelatedTopics(topic, 5);

			ViewData["Page"] = page;
			//Defines that the message url should be full
			ViewData["FullUrl"] = true;

			return View(topic);
		} 
		#endregion

		#region Latest Messages
		[ValidateReadAccess]
		public ActionResult LatestMessages(int id, string name)
		{
			var topic = _service.Get(id, name);

			if (topic == null)
			{
				return ResultHelper.NotFoundResult(this);
			}

			topic.Messages = _messageService.GetByTopicLatest(id);

			return ResultHelper.XmlViewResult(this, topic);
		}
		#endregion

		#region Add
		[HttpGet]
		[RequireAuthorization]
		[PreventFlood]
		public ActionResult Add(string forum)
		{
			var f = _forumService.Get(forum);
			if (!f.HasPostAccess(Role))
			{
				return ResultHelper.ForbiddenResult(this);
			}

			var topic = new Topic();
			topic.Forum = f;
			//Default the right access to its parent. If less it will be overriden.
			topic.ReadAccessRole = f.ReadAccessRole;
			//Default, It can be less than its parent
			topic.PostAccessRole = f.PostAccessRole;
			var roles = _userService.GetRoles().Where(x => x.Key <= Role);
			ViewBag.UserRoles = new SelectList(roles, "Key", "Value");

            //if the topic type relation is null, set default value
            if (topic.topicTypeRel == null)
            {
                topic.topicTypeRel = new TopicTypeRelation();
                topic.topicTypeRel.Type = new TopicType();
                topic.topicTypeRel.Type.TypeID = 0;
            }
            if (topic.Forum.topicTypeList == null)
                topic.Forum.topicTypeList = new System.Collections.Generic.List<TopicType>();

			return View("Edit", topic);
		}

		[HttpPost]
		[RequireAuthorization]
		[ValidateInput(false)]
		[PreventFlood(typeof(RedirectToRouteResult))]
		[ValidateAntiForgeryToken]
		public ActionResult Add(string forum, [Bind(Prefix = "", Exclude = "Id,Forum")] Topic topic, bool notify, string email)
		{
			try
			{
				SubscriptionHelper.SetNotificationEmail(notify, email, Session, Config, _userService);
				
				topic.Forum = _forumService.Get(forum);
				if (topic.Forum == null)
				{
					return ResultHelper.NotFoundResult(this);
				}
				if (!topic.Forum.HasPostAccess(Role))
				{
					return ResultHelper.ForbiddenResult(this);
				}

				topic.User = new User(User.Id, User.UserName);
                topic.ShortName = DateTime.Now.ToString("yyMMddHHmmssfff").ToUrlSegment(64);
				topic.IsSticky = (topic.IsSticky && this.User.Role >= UserRole.Moderator);
				if (topic.Description != null)
				{
					topic.Description = topic.Description.SafeHtml().ReplaceValues();
				}

				if (ModelState.IsValid)
				{
					_service.Create(topic, Request.UserHostAddress);
					SubscriptionHelper.Manage(notify, topic.Id, this.User.Id, this.User.Guid, this.Config, _topicSubscriptionService);
					return RedirectToRoute(new { action = "Detail", controller = "Topics", id = topic.Id, name = topic.ShortName, forum = forum, page = 0 });
				}
			}
			catch (ValidationException ex)
			{
				this.AddErrors(this.ModelState, ex);
			}
			var roles = _userService.GetRoles().Where(x => x.Key <= Role);
			ViewBag.UserRoles = new SelectList(roles, "Key", "Value");
			return View("Edit", topic);
		}
		#endregion

		#region Edit
		[HttpGet]
		[RequireAuthorization]
		[ValidateReadAccess]
		public ActionResult Edit(int id, string name, string forum)
		{
			Topic topic = _service.Get(id, name);

			if (topic == null)
			{
				return ResultHelper.NotFoundResult(this);
			}
			#region Check if user can edit
			if (this.User.Role < UserRole.Moderator && this.User.Id != topic.User.Id)
			{
				return ResultHelper.ForbiddenResult(this);
			}
			#endregion
			ViewBag.IsEdit = true;
			ViewBag.notify = SubscriptionHelper.IsUserSubscribed(id, this.User.Id, this.Config, _topicSubscriptionService);
			var roles = _userService.GetRoles().Where(x => x.Key <= Role);
			ViewBag.UserRoles = new SelectList(roles, "Key", "Value");

			return View(topic);
		}

		[HttpPost]
		[RequireAuthorization]
		[ValidateInput(false)]
		[ValidateAntiForgeryToken]
		public ActionResult Edit(int id, string name, string forum, [Bind(Prefix = "", Exclude = "Forum")] Topic topic, bool notify, string email)
		{
			topic.Id = id;
			topic.Forum = _forumService.Get(forum);
			if (topic.Forum == null)
			{
				return ResultHelper.NotFoundResult(this);
			}
			if (!topic.Forum.HasPostAccess(Role))
			{
				return ResultHelper.ForbiddenResult(this);
			}

			#region Check if user can edit
			var originalTopic = _service.Get(id);
			if (User.Role < UserRole.Moderator)
			{
				//If the user is not moderator or admin: Check if the user that created of the topic is the same as the logged user
				if (User.Id != originalTopic.User.Id)
				{
					return ResultHelper.ForbiddenResult(this);
				}
				//The user cannot edit the sticky property
				topic.IsSticky = originalTopic.IsSticky;
			}
			else if (!originalTopic.HasReadAccess(Role))
			{
				return ResultHelper.ForbiddenResult(this);
			}
			#endregion

			try
			{
				SubscriptionHelper.SetNotificationEmail(notify, email, Session, Config, _userService);

				topic.User = new User(User.Id, User.UserName);
				topic.ShortName = name;
				if (topic.Description != null)
				{
					topic.Description = topic.Description.SafeHtml().ReplaceValues();
				}
				_service.Edit(topic, Request.UserHostAddress);
				SubscriptionHelper.Manage(notify, topic.Id, User.Id, this.User.Guid, Config, _topicSubscriptionService);
				return RedirectToRoute(new{action="Detail",controller="Topics",id=topic.Id,name=name,forum=forum});
			}
			catch (ValidationException ex)
			{
				this.AddErrors(this.ModelState, ex);
			}
			ViewBag.IsEdit = true;
			var roles = _userService.GetRoles().Where(x => x.Key <= Role);
			ViewBag.UserRoles = new SelectList(roles, "Key", "Value");

			return View(topic);
		}
		#endregion

		#region Client Paging
		/// <summary>
		/// Gets an amount (by config) of message items starting from initIndex
		/// </summary>
		[HttpPost]
		[ValidateReadAccess(RefuseOnFail=true)]
		public ActionResult PageMore(int id, string name, string forum, int from, int initIndex)
		{
			//load topic that contains messages
			var topic = _service.GetMessagesFrom(id, from, Config.UI.MessagesPerPage, initIndex);
			if (topic == null)
			{
				return ResultHelper.NotFoundResult(this);
			}
			//Defines that the message url just be the anchor name
			ViewData["FullUrl"] = false;

			return View(false, topic);
		}

		[HttpPost]
		[ValidateReadAccess(RefuseOnFail = true)]
		public ActionResult PageUntil(int id, string name, string forum, int firstMsg, int lastMsg, int initIndex)
		{
			//load topic that contains messages
			var topic = _service.GetMessages(id, firstMsg, lastMsg, initIndex);
			if (topic == null)
			{
				return ResultHelper.NotFoundResult(this);
			}
			//Defines that the message url just be the anchor name
			ViewData["FullUrl"] = false;

			return View(false, "PageMore", topic);
		}
		#endregion

		#region Short Urls
		/// <summary>
		/// Gets the topic by id and redirects to the long relative url
		/// </summary>
		/// <param name="id"></param>
		/// <returns></returns>
		public ActionResult ShortUrl(int id)
		{
			Topic t = _service.Get(id);
			if (t == null)
			{
				return ResultHelper.NotFoundResult(this, true);
			}

			return new MovedPermanentlyResult(new{action="Detail", controller="Topics", id=t.Id, name=t.ShortName,forum=t.Forum.ShortName});
		}
		#endregion

		#region Move / Close / Delete
		#region Delete
		[HttpPost]
		[RequireAuthorization]
		[ValidateAntiForgeryToken]
		public ActionResult Delete(int id, string name, string forum)
		{
			#region Check if user can edit
			var originalTopic = _service.Get(id);
			if (this.User.Role < UserRole.Moderator)
			{
				//Check if the user that created of the topic is the same as the logged user
				if (this.User.Id != originalTopic.User.Id)
				{
					return ResultHelper.ForbiddenResult(this);
				}
			}
			else if (!originalTopic.HasReadAccess(Role))
			{
				return ResultHelper.ForbiddenResult(this);
			}			
			#endregion

			_service.Delete(id, this.User.Id, Request.UserHostAddress);

			return Json(new { nextUrl=Url.Action("Detail", "Forums", new{ forum = forum})});
		}
		#endregion

		#region Move topic
		[HttpGet]
		[RequireAuthorization(UserRole.Moderator)]
		[ValidateReadAccess]
		public ActionResult Move(int id, string name)
		{
			var topic = _service.Get(id, name);
			if (topic == null)
			{
				return ResultHelper.NotFoundResult(this);
			}

			ViewBag.Categories = _forumService.GetList(Role);
			return View(topic);
		}

		[HttpPost]
		[RequireAuthorization(UserRole.Moderator)]
		[ValidateAntiForgeryToken]
		public ActionResult Move(int id, string name, [Bind(Prefix = "", Exclude = "Id")] Topic t)
		{
			Topic savedTopic = _service.Move(id, t.Forum.Id, this.User.Id, Request.UserHostAddress);
			return RedirectToAction("Detail", new
			{
				forum = savedTopic.Forum.ShortName
			});
		}
		#endregion

		#region Close
		/// <summary>
		/// Disallow replies on the topic
		/// </summary>
		[HttpPost]
		[RequireAuthorization]
		[ValidateAntiForgeryToken]
		public ActionResult CloseReplies(int id, string name)
		{
			#region Check if user can edit
			var originalTopic = _service.Get(id);
			if (Role < UserRole.Moderator)
			{
				//Check if the user that created of the topic is the same as the logged user
				if (this.User.Id != originalTopic.User.Id)
				{
					return ResultHelper.ForbiddenResult(this);
				}
			}
			else if (!originalTopic.HasReadAccess(Role))
			{
				return ResultHelper.ForbiddenResult(this);
			}
			#endregion

			_service.Close(id, this.User.Id, Request.UserHostAddress);

			return new EmptyResult();
		}

		/// <summary>
		/// Allow replies on the topic
		/// </summary>
		[HttpPost]
		[RequireAuthorization]
		[ValidateAntiForgeryToken]
		public ActionResult OpenReplies(int id, string name)
		{
			#region Check if user can edit
			var originalTopic = _service.Get(id);
			if (this.User.Role < UserRole.Moderator)
			{
				//Check if the user that created of the topic is the same as the logged user
				if (this.User.Id != originalTopic.User.Id)
				{
					return ResultHelper.ForbiddenResult(this);
				}
			}
			else if (!originalTopic.HasReadAccess(Role))
			{
				return ResultHelper.ForbiddenResult(this);
			}
			#endregion

			_service.Open(id, this.User.Id, Request.UserHostAddress);

			return new EmptyResult();
		}
		#endregion
		#endregion

        #region uploadFile
        public ActionResult UploadFile()
        {
            String aspxUrl = Request.Path.Substring(0, Request.Path.LastIndexOf("/") + 1);

            //文件保存目录路径
            String savePath = "../attached/";

            //文件保存目录URL
            String saveUrl = aspxUrl + "../attached/";

            //定义允许上传的文件扩展名
            Hashtable extTable = new Hashtable();
            extTable.Add("image", "gif,jpg,jpeg,png,bmp");
            extTable.Add("flash", "swf,flv");
            extTable.Add("media", "swf,flv,mp3,wav,wma,wmv,mid,avi,mpg,asf,rm,rmvb");
            extTable.Add("file", "doc,docx,xls,xlsx,ppt,htm,html,txt,zip,rar,gz,bz2");

            //最大文件大小
            int maxSize = 1000000;

            HttpPostedFileBase imgFile = Request.Files["imgFile"];
            if (imgFile == null)
            {
                return GetError("请选择文件。");
            }

            String dirPath = Server.MapPath(savePath);
            if (!Directory.Exists(dirPath))
            {
                return GetError("上传目录不存在。");
            }

            String dirName = Request.QueryString["dir"];
            if (String.IsNullOrEmpty(dirName))
            {
                dirName = "image";
            }
            if (!extTable.ContainsKey(dirName))
            {
                return GetError("目录名不正确。");
            }

            String fileName = imgFile.FileName;
            String fileExt = Path.GetExtension(fileName).ToLower();

            if (imgFile.InputStream == null || imgFile.InputStream.Length > maxSize)
            {
                return GetError("上传文件大小超过限制。");
            }

            if (String.IsNullOrEmpty(fileExt) || Array.IndexOf(((String)extTable[dirName]).Split(','), fileExt.Substring(1).ToLower()) == -1)
            {
                return GetError("上传文件扩展名是不允许的扩展名。\n只允许" + ((String)extTable[dirName]) + "格式。");
            }

            System.Drawing.Image img = System.Drawing.Image.FromStream(imgFile.InputStream, false);
            //缩放图片      
            if (img.Width > 600)
                img = GetOutputSizeImage(img, 600);

            //创建文件夹
            dirPath += dirName + "/";
            saveUrl += dirName + "/";
            if (!Directory.Exists(dirPath))
            {
                Directory.CreateDirectory(dirPath);
            }
            String ymd = DateTime.Now.ToString("yyyyMMdd", DateTimeFormatInfo.InvariantInfo);
            dirPath += ymd + "/";
            saveUrl += ymd + "/";
            if (!Directory.Exists(dirPath))
            {
                Directory.CreateDirectory(dirPath);
            }

            String newFileName = DateTime.Now.ToString("yyyyMMddHHmmss_ffff", DateTimeFormatInfo.InvariantInfo) + fileExt;
            String filePath = dirPath + newFileName;

            img.Save(filePath);

            String fileUrl = saveUrl + newFileName;

            Hashtable hash = new Hashtable();
            hash["error"] = 0;
            hash["url"] = fileUrl;
            return Json(hash, "text/html;charset=UTF-8");
        }

        private Image GetOutputSizeImage(Image imgSource, int MAX_WIDTH)
        {
            Image imgOutput = imgSource;

            Size size = new Size(imgSource.Width, imgSource.Height);
            if (imgSource.Width <= 3 || imgSource.Height <= 3) return imgSource; //3X3大小的图片不转换 

            if (imgSource.Width > MAX_WIDTH || imgSource.Height > MAX_WIDTH)
            {
                double rate = MAX_WIDTH / (double)imgSource.Width;

                if (imgSource.Height * rate > MAX_WIDTH)
                    rate = MAX_WIDTH / (double)imgSource.Height;

                size.Width = Convert.ToInt32(imgSource.Width * rate);
                size.Height = Convert.ToInt32(imgSource.Height * rate);

                imgOutput = imgSource.GetThumbnailImage(size.Width, size.Height, null, IntPtr.Zero);
            }

            return imgOutput;
        }

        private ActionResult GetError(string message)
        {
            Hashtable hash = new Hashtable();
            hash["error"] = 1;
            hash["message"] = message;

            return Json(hash, "text/html;charset=UTF-8");
        }
        #endregion
    }
}
